local JerryErrorFrame = CreateFrame("Frame", "JerryErrorFrame", UIParent)

JerryErrorFrame:SetScript("OnEvent", function (self, event, ...)
	local handler = self[event]
	if handler then handler(self, event, ...) end
end)

JerryErrorFrame:RegisterEvent"ADDON_LOADED"
function JerryErrorFrame:ADDON_LOADED(_, addon)
	if addon == "!Error" then
		if not JerryErrorDB then
			self:Clear()
		else
			self.Errors = JerryErrorDB
			self.ErrorCount = 0
			self.ErrorIndex = 1
		end

		self:UnregisterEvent"ADDON_LOADED"
		self.ADDON_LOADED = nil
	end
end

JerryErrorFrame:RegisterEvent"PLAYER_LOGIN"
function JerryErrorFrame:PLAYER_LOGIN()
	local ldb = LibStub and LibStub("LibDataBroker-1.1", true)
	if ldb then
		self.launcher = ldb:NewDataObject("JerryError", {
		    type = "launcher",
		    icon = "Interface\\Icons\\INV_Gizmo_FelIronBomb",
			label = "JerryErrorFrame",
			value = self.ErrorCount,
			suffix = " errors",
		    OnClick = function(_, button)
				if button == "LeftButton" then
					if JerryErrorFrame:IsShown() then
						JerryErrorFrame:Hide()
					else
						JerryErrorFrame:Show()
					end
				end
			end,
			OnTooltipShow = function (tooltip)
				tooltip:AddLine("JerryError")
				tooltip:AddLine(JerryErrorFrame.launcher.text)
				for i = 1, 5 do
					local error = JerryErrorDB[#JerryErrorDB + 1 - i]
					if error then
						tooltip:AddDoubleLine(error.msg, error.count)
					end
				end
			end,
		})
	end
	self:UnregisterEvent"PLAYER_LOGIN"
	self.PLAYER_LOGIN = nil
end

JerryErrorFrame:SetPoint("TOP", UIParent, "TOP", 0, -100)
JerryErrorFrame:SetFrameStrata"TOOLTIP"
JerryErrorFrame:SetHeight(280)
JerryErrorFrame:SetWidth(500)
JerryErrorFrame:SetBackdrop{
	bgFile = "Interface/Tooltips/ChatBubble-Background",
	edgeFile = "Interface/Tooltips/ChatBubble-BackDrop",
	tile = true, tileSize = 32, edgeSize = 32,
	insets = { left = 32, right = 32, top = 32, bottom = 32 }
}
JerryErrorFrame:SetBackdropColor(0,0,0, 1)
JerryErrorFrame:SetScript("OnShow", function (self) self:ShowError() end)
JerryErrorFrame:SetScript("OnHide", function (self) self.Text:SetText"" end)
JerryErrorFrame:SetClampedToScreen(true)

function JerryErrorFrame:ErrorBell()
	local time = GetTime()
	local prev_time = self.bellTime or 0
	if time - prev_time > 1 then
		PlaySoundFile"Interface\\AddOns\\!Error\\error.wav"
		self.bellTime = time
	end
end

function JerryErrorFrame:AddError(msg)
	self.ErrorCount = self.ErrorCount + 1
	if self.launcher then
		self.launcher.value = self.ErrorCount
		self.launcher.text = ("%d errors"):format(self.ErrorCount)
		self.launcher.icon = "Interface\\Icons\\INV_Gizmo_SuperSapperCharge"
	end
	local stack = debugstack(4, 20, 20)
	local date = date"%Y/%m/%d %H:%M:%S"
	local Errors = self.Errors
	local count = #Errors
	for i = count, 1, -1 do
		local error = Errors[i]
		if error and error.stack == stack and msg == error.msg then
			error.date = date
			error.count = error.count + 1
			if i ~= count then
				table.remove(Errors, i)
				Errors[count] = error
				self.ErrorIndex = count
			end
			self:ErrorBell()
			self:ShowError()
			return
		end
	end
	if count >= 50 then
		table.remove(Errors, 1)
	end
	count = count + 1
	Errors[count] = {
		msg = msg,
		stack = stack,
		date = date,
		count = 1,
	}
	self.ErrorIndex = count
	self:ErrorBell()
	self:ShowError()
end

function JerryErrorFrame:ShowError(index)
	index = index or self.ErrorIndex
	local error = self.Errors[index] or self.Errors[index]
	if not error then
		index = 1
		error = self.Errors[index]
	end
	if error then
		self.ErrorIndex = index
		self.Text:SetText(string.format([[
Date: %s
Count: %d
Message: %s
Backtrace:
%s
]], error.date, error.count, error.msg, error.stack))
	else
		self.Text:SetText"No Error"
	end
	if not self:IsShown() then
		self:Show()
	end
	if index <= 1 then
		self.Prev:Disable()
	else
		self.Prev:Enable()
	end
	if index >= #self.Errors then
		self.Next:Disable()
	else
		self.Next:Enable()
	end
end

function JerryErrorFrame:NextError()
	local index = self.ErrorIndex
	if index < #self.Errors then
		self.ErrorIndex = index + 1
		if self:IsShown() then
			self:ShowError()
		end
	end
end

function JerryErrorFrame:PrevError()
	local index = self.ErrorIndex
	if index > 1 then
		self.ErrorIndex = index - 1
		if self:IsShown() then
			self:ShowError()
		end
	end
end

local Close = CreateFrame("Button", "$parentClose", JerryErrorFrame, "OptionsButtonTemplate")
Close:SetText"Close"
Close:SetPoint("BOTTOMRIGHT", -10, 10)
Close:SetScript("OnClick", function (self) self:GetParent():Hide() end)

local Next = CreateFrame("Button", "$parentNext", JerryErrorFrame, "OptionsButtonTemplate")
Next:SetText"Next >"
Next:SetPoint("BOTTOMRIGHT", Close, "BOTTOMLEFT", -5, 0)
Next:SetScript("OnClick", function (self) self:GetParent():NextError() end)

local Prev = CreateFrame("Button", "$parentPrev", JerryErrorFrame, "OptionsButtonTemplate")
Prev:SetText"< Prev"
Prev:SetPoint("BOTTOMRIGHT", Next, "BOTTOMLEFT", -5, 0)
Prev:SetScript("OnClick", function (self) self:GetParent():PrevError() end)

local Scroll = CreateFrame("ScrollFrame", "$parentScroll", JerryErrorFrame, "UIPanelScrollFrameTemplate")
Scroll:SetPoint("TOPLEFT", 20, -20)
Scroll:SetPoint("RIGHT", -30, 0)
Scroll:SetPoint("BOTTOM", Close, "TOP", 0, 10)

local Text = CreateFrame("EditBox", "$parentText", Scroll)
Text:SetWidth(450)
Text:SetHeight(85)
Text:SetMultiLine(true)
Text:SetAutoFocus(false)
Text:SetFontObject(GameFontHighlight)
Text:SetScript("OnEscapePressed", function (self) self:ClearFocus() end)
Text:SetScript("OnTextChanged", function (self) self:GetParent():GetParent():ShowError() end)

Scroll:SetScrollChild(Text)

JerryErrorFrame.Close = Close
JerryErrorFrame.Next = Next
JerryErrorFrame.Prev = Prev
JerryErrorFrame.Scroll = Scroll
JerryErrorFrame.Text = Text

local Grabber = CreateFrame"Frame"
Grabber:SetScript("OnEvent", function (self, event, addon, func)
	JerryErrorFrame:AddError(("Addon %q tried to call the %s function %q"):format(
		addon or "*unknown*", 
		event == "ADDON_ACTION_BLOCKED" and "protected" or "forbidden", func))
end)

seterrorhandler(function (msg) JerryErrorFrame:AddError(msg) end)

Grabber:RegisterEvent"ADDON_ACTION_BLOCKED"
Grabber:RegisterEvent"ADDON_ACTION_FORBIDDEN"
UIParent:UnregisterEvent"ADDON_ACTION_FORBIDDEN"
UIParent:UnregisterEvent"ADDON_ACTION_BLOCKED"

JerryErrorFrame:Hide()

function JerryErrorFrame:Clear()
	JerryErrorDB = {}
	self.Errors = JerryErrorDB
	self.ErrorIndex = 1
	self.ErrorCount = 0
	if self.launcher then
		self.launcher.value = 0
	end
end

SlashCmdList.JERRY_ERROR = function (input)
	if input == "clear" then
		JerryErrorFrame:Clear()
	else
		JerryErrorFrame:Show()
	end
end
SLASH_JERRY_ERROR1 = "/error"
